/*
    Author: Christopher Diggins
    License: MIT Licence 1.0
    Website: http://www.heron-language.com
*/

#ifndef LLSCHEME_LEVEL0_HPP
#define LLSCHEME_LEVEL0_HPP

namespace lls
{		
	enum EnumPrim
	{
		prim_inc,
		prim_dec,
		prim_neg,
		prim_add,
		prim_sub,
		prim_mul,
		prim_div,
		prim_mod,
		prim_gt,
		prim_lt,
		prim_gteq,
		prim_lteq,
		prim_eq,
		prim_neq,
		prim_and,
		prim_or,
		prim_xor,
		prim_cond,
		prim_null,
		prim_nil,
		prim_unused
	};

	const char* PrimToStr(EnumPrim n) {
		switch (n) 
		{
		case prim_inc:return "inc";
		case prim_dec:return "dec";
		case prim_neg:return "neg";
		case prim_add:return "add";
		case prim_sub:return "sub";
		case prim_mul:return "mul";
		case prim_div:return "div";
		case prim_mod:return "mod";
		case prim_gt:return "gt";
		case prim_lt:return "lt";
		case prim_gteq:return "gteq";
		case prim_lteq:return "lteq";
		case prim_eq:return "eq";
		case prim_neq:return "neq";
		case prim_and:return "and";
		case prim_or:return "or";
		case prim_xor:return "xor";
		case prim_cond:return "cond";
		case prim_null:return "null";
		case prim_nil:return "nil";
		case prim_unused:return "_UNUSED_";
		default:
			Error("Unrecognized enumeration " + ToStr(n));
		}
		return "";
	}

	struct Args;

	struct Name : Atom {
		Name(ConstString s) 
			: Atom(s) 
		{ }
		Name() 
		{ }
		String ToString() { return "name=" + data; }
		SExpr* NewInstance() { return new Name(data); }		
	};

	struct Type : Atom {
		Type(ConstString s) 
			: Atom(s) 
		{ }
		Type()
		{ }
		String ToString() { return "type=" + data; }
		SExpr* NewInstance() { return new Type(data); }		
	};
	
	struct Declare : SExpr {
		String Label() { return "declare"; }
		SExpr* NewInstance() { return new Declare(); }		
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		Type* GetType() { return children[1]->As<Type>(); }
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct SetField : SExpr {
		String Label() { return "setfield"; }
		SExpr* NewInstance() { return new SetField(); }		
		SExpr* Object() { return children[0]; }
		Name* GetNameExpr() { return children[1]->As<Name>(); }
		SExpr* GetVal() { return children[2]; }
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct GetField : SExpr {
		String Label() { return "getfield"; }
		SExpr* NewInstance() { return new GetField(); }		
		SExpr* Object() { return children[0]; }
		Name* GetNameExpr() { return children[1]->As<Name>(); }
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct New : SExpr {
		String Label() { return "new"; }
		SExpr* NewInstance() { return new New(); }		
		Type* GetType() { return children[0]->As<Type>(); }
		Args* GetArgs() { return children[1]->As<Args>(); }		
	};

	struct Set : SExpr {
		Set() {
		}
		Set(String s, SExpr* x) {
			AddChild(new Name(s));
			AddChild(x);
		}
		String Label() { return "set"; }
		SExpr* NewInstance() { return new Set(); }		
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		SExpr* GetVal() { return children[1]->As<SExpr>(); }
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct Get : SExpr {
		Get() {
		}
		Get(String s) {
			AddChild(new Name(s));
		}
		String Label() { return "get"; }
		SExpr* NewInstance() { return new Get(); }		
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct GetAt : SExpr {
		String Label() { return "getat"; }
		SExpr* NewInstance() { return new GetAt(); }
		SExpr* Collection() { return children[0]; }
		SExpr* Index() { return children[1]; }
	};

	struct SetAt : SExpr {
		String Label() { return "setat"; }
		SExpr* NewInstance() { return new SetAt(); }
		SExpr* Collection() { return children[0]; }
		SExpr* Index() { return children[1]; }
		SExpr* GetVal() { return children[2]; }
	};

	struct Literal : Atom {
		Literal(ConstString val) :
			Atom(val)
		{ }
		String ToString() { return "literal=" + data; }
		SExpr* NewInstance() { return new Literal(data); }		
	};

	template<typename T>
	struct PLiteral : Literal {
		T param;
		PLiteral(T x) 
			: param(x), Literal(ToStr(x))
		{ }
		SExpr* NewInstance() { return new PLiteral(param); }		
		Name* GetVal() { return children[0]->As<Name>(); }
		String GetValStr() { return GetVal()->Label(); }
		T Param() { return param; }
	};

	// TODO: rename.
	struct Var : SExpr {
		String Label() { return "var"; }
		SExpr* NewInstance() { return new Var(); }
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		Type* GetType() { return children[1]->As<Type>(); }
		SExpr* GetVal() { return children[2]; }
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct Return : SExpr {
		String Label() { return "return"; }
		SExpr* NewInstance() { return new Return(); }		
		SExpr* GetVal() { return children[0]; }
	};

	struct NoOp : SExpr {
		String Label() { return "noop"; }
		SExpr* NewInstance() { return new NoOp(); }		
	};

	struct Seq : SExpr {	
		SExpr* NewInstance() { return new Seq(); }		
		String Label() { return "begin"; }
	};

	struct If : SExpr {
		String Label() { return "if"; }
		SExpr* NewInstance() { return new If(); }		
		SExpr* Cond() { return children[0]; }
		SExpr* TrueBranch() { return children[1]; }
		SExpr* FalseBranch(){ return (children.size() > 2) ? children[2] : NULL; }
	};

	struct Args : SExpr {
		String Label() { return "args"; } 
		SExpr* NewInstance() { return new Args(); }
	};

	struct Invoke : SExpr {
		String Label() { return "invoke"; }
		SExpr* NewInstance() { return new Invoke(); }		
		SExpr* Object() { return children[0]; }
		Name* GetNameExpr() { return children[1]->As<Name>(); }
		Args* GetArgs() { return children[2]->As<Args>(); }
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct Apply : SExpr {
		String Label() { return "apply"; }
		SExpr* NewInstance() { return new Apply(); }		
		SExpr* Func() { return children[0]; }
		Args* GetArgs() { return children[1]->As<Args>(); }
	};

	struct While : SExpr {
		String Label() { return "while"; }
		SExpr* NewInstance() { return new While(); }		
		SExpr* Cond() { return children[0]; }
		SExpr* Body() { return children[1]; }
	};

	struct ForEachIndex : SExpr {
		String Label() { return "foreachindex"; }		
		SExpr* NewInstance() { return new ForEachIndex(); }		
		Name* GetNameExpr() { return children[0]->As<Name>(); } 
		Type* GetType() { return children[1]->As<Type>(); } 
		SExpr* List() { return children[2]; }
		Seq* Body() { return children[3]->As<Seq>(); } 
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct ForEach : SExpr {
		String Label() { return "foreach"; }		
		SExpr* NewInstance() { return new ForEach(); }		
		Name* GetNameExpr() { return children[0]->As<Name>(); } 
		Type* GetType() { return children[1]->As<Type>(); } 
		SExpr* List() { return children[2]; }
		SExpr* Body() { return children[3]; } 
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct For : SExpr {
		String Label() { return "for"; }
		SExpr* NewInstance() { return new For(); }		
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		Type* GetType() { return children[1]->As<Type>(); }
		SExpr* Init() { return children[2]; }
		SExpr* Inv() { return children[3]; }
		SExpr* Next() { return children[4]; }
		SExpr* Body() { return children[5]; }
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct Formal : SExpr  {
		String Label() { return "formal"; }
		SExpr* NewInstance() { return new Formal(); }		
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		Type* GetType() { return children[1]->As<Type>(); }
		String GetNameStr() { return GetNameExpr()->Label(); } 
		String GetTypeStr() { return GetType()->Label(); } 
	};

	struct Formals : SExpr {		
		String Label() { return "formals"; }
		SExpr* NewInstance() { return new Formals(); }		
		Formal* GetFormal(int i) { return GetChild(i)->As<Formal>(); }
	};

	struct Lambda : SExpr  {
		String Label() { return "lambda"; }
		SExpr* NewInstance() { return new Lambda(); }		
		Formals* GetFormals() { return children[0]->As<Formals>(); }
		Type* GetType() { return children[1]->As<Type>(); }
		Seq* Body() { return children[2]->As<Seq>(); } 
	};

	struct TransformAt : SExpr {
		String Label() { return "transformat"; }
		SExpr* NewInstance() { return new TransformAt(); }
		SExpr* Collection() { return children[0]; }
		SExpr* Index() { return children[1]; }
		Lambda* GetLambda() { return children[2]->As<Lambda>(); }
	};

	struct Define : SExpr  {
		String Label() { return "define"; } 
		SExpr* NewInstance() { return new Define(); }		
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		Formals* GetFormals() { return children[1]->As<Formals>(); }
		Type* GetType() { return children[2]->As<Type>(); }
		Seq* Body() { return children[3]->As<Seq>(); } 
		String GetNameStr() { return GetNameExpr()->Label(); } 
	};

	struct Primitive : SExpr  {
		Primitive(EnumPrim n) 
			: id(n) 
		{ }
		Primitive(EnumPrim n, SExpr* x) 
			: id(n) 
		{ 
			AddChild(x);
		}
		Primitive(EnumPrim n, SExpr* x0, SExpr* x1) 
			: id(n) 
		{ 
			AddChild(x0);
			AddChild(x1);
		}
		EnumPrim id;
		String Label() { return PrimToStr((EnumPrim)id); }
		EnumPrim Id() { return id; }
		String IdStr() { return PrimToStr(Id()); } 
		SExpr* NewInstance() { return new Primitive(id); }
	};

	struct Inherits : SExpr {
		String Label() { return "inherits"; }
		SExpr* NewInstance() { return new Inherits(); }
	};

	struct Implements : SExpr {
		String Label() { return "implements"; }
		SExpr* NewInstance() { return new Implements(); }
	};

	struct Method : SExpr {
		String Label() { return "method"; } 
		SExpr* NewInstance() { return new Method(); }		
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		Formals* GetFormals() { return children[1]->As<Formals>(); }
		Type* GetType() { return children[2]->As<Type>(); }
		String GetNameStr() { return GetNameExpr()->Label(); } 
		Seq* GetBody() { return children[3]->As<Seq>(); }
	};

	struct Methods : SExpr {
		String Label() { return "methods"; }
		SExpr* NewInstance() { return new Methods(); }
	};

	struct Field : SExpr {
		String Label() { return "field"; }
		SExpr* NewInstance() { return new Field(); }
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		Type* GetType() { return children[1]->As<Type>(); }
		String GetNameStr() { return GetNameExpr()->Label(); } 
		String GetTypeStr() { return GetType()->Label(); } 
	};

	struct Fields : SExpr {
		String Label() { return "fields"; }
		SExpr* NewInstance() { return new Fields(); }
	};

	struct Class : SExpr {
		String Label() { return "class"; }
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		String GetNameStr() { return GetNameExpr()->Label(); }
		Inherits* GetInherits() { return children[1]->As<Inherits>(); }
		Implements* GetImplements() { return children[2]->As<Implements>(); }
		Methods* GetMethods() { return children[3]->As<Methods>(); }
		Fields* GetFields() { return children[4]->As<Fields>(); }
		SExpr* NewInstance() { return new Class(); } 
	};

	struct Module : SExpr { 		
		String Label() { return "module"; }
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		String GetNameStr() { return GetNameExpr()->Label(); }
		SExpr* NewInstance() { return new Module(); } 
	};

	struct Ast : SExpr {
		String Label() { return "ast"; }
		SExpr* NewInstance() { return new Ast(); } 
	};

	struct Program : SExpr { 		
		String Label() { return "program"; }
		Name* GetNameExpr() { return children[0]->As<Name>(); }
		String GetNameStr() { return GetNameExpr()->Label(); }
		Seq* GetStatements() { return children[1]->As<Seq>(); }
		SExpr* NewInstance() { return new Program(); } 
	};

	struct Import : SExpr {
		String Label() { return "import"; }
		Module* GetModule() { return children[0]->As<Module>(); }
		SExpr* NewInstance() { return new Import(); } 
	};

	//==========================================================================================

	void RemoveReturns(SExpr* expr) {
		// TODO: rewrite the thing
	}

	void PrintAsXML(SExpr* expr) {
		// TODO:
	}

	template<typename T>
	bool DoesNameMatch(SExpr* x, ConstString s) {
		return x->Is<T>() && x->As<T>()->GetNameStr() == s;
	}

	bool IsMatchingVar(SExpr* x, ConstString s) {
		return DoesNameMatch<Var>(x, s);
	}

	bool ContainsPtr(SExpr* x, SExpr* y) {
		if (x == y) 
			return true;
		for (int i=0; i < x->GetNumChildren(); ++i) 
			if (ContainsPtr(x->GetChild(i), y))
				return true;
		return false;
	}
	
	bool IsGet(SExpr* x, ConstString s) {
		return x->Is<Get>() && x->As<Get>()->GetNameStr() == s;
	}

	bool ContainsName(SExpr* x, ConstString s) {
		if (IsAtom(x, s))
			return true;
		for (int i=0; i < x->GetNumChildren(); ++i) 
			if (ContainsName(x->GetChild(i), s))
				return true;
		return false;
	}
	
	template<typename T>
	bool ContainsType(SExpr* x) {
		if (x->Is<T>()) 
			return true;
		for (int i=0; i < x->GetNumChildren(); ++i)
			if (ContainsType<T>(x->GetChild(i)))
				return true;
		return false;
	}

	void Replace(SExpr*& x, SExpr* y) {
		int n = x->GetIndex();
		x->GetParent()->ReplaceChild(n, y);
		x = y;
	}

	bool IsLoopConstruct(NonNull<SExpr> x) {
		return x->Is<While>() || x->Is<For>() || x->Is<ForEach>();
	}

	bool IsSpecialConstruct(NonNull<SExpr> x) {
		return IsLoopConstruct(x) || x->Is<If>() || x->Is<Lambda>() || x->Is<Define>();
	}
}

#endif
